
#include <stdlib.h>

#include "../util/macros.h"

#include "image.h"

/* ========================================================================== */
static void constructor(Image * img, int width, int height, ImagePalette palette);
static void set_pixel_gray(Image * img, int x, int y, int px);
static int get_pixel_gray(Image * img, int x, int y);
static void set_pixel_rgb(Image * img, int x, int y, int px);
static int get_pixel_rgb(Image * img, int x, int y);
/* ========================================================================== */

/* Constructors and Destructor ============================================== */
Image * image_new(int width, int height, ImagePalette palette) {
	Image * img;

	/* alloc */
	img = (Image *) calloc(1, sizeof (Image));

	/* constructor */
	constructor(img, width, height, palette);
	img->data = (unsigned char *) calloc(1, img->size);


	//image_get_interface().delete(img);

	/* return */
	return img;
}

void image_delete(Image * img) {
	free(img->data);
	free(img);
}

void image_assign_data(Image * img, unsigned char * data, int width, int height, ImagePalette palette) {
	constructor(img, width, height, palette);
	img->data = data;
}

/* Getters and Setters ====================================================== */
void image_set_pixel(Image * img, int x, int y, int px) {

	int idx;

	/* constructor */
	switch (img->palette) {
		case IMAGE_PALETTE_GRAY:
			img->data[x * img->width + y] = (unsigned char) px;

			break;
		case IMAGE_PALETTE_YUV420p:


			break;
		case IMAGE_PALETTE_RGB:
			idx = (x * img->width + y) * 3;

			img->data[ idx ] = px >> 16;
			img->data[ idx + 1 ] = px >> 8;
			img->data[ idx + 2 ] = px;

			break;
		default:
			exit(0);
			return;
	}
}

int image_get_pixel(Image * img, int x, int y) {
	int px = 0;
	int idx;

	/* constructor */
	switch (img->palette) {
		case IMAGE_PALETTE_GRAY:
			px = img->data[x * img->width + y];

			break;
		case IMAGE_PALETTE_YUV420p:


			break;
		case IMAGE_PALETTE_RGB:
			idx = (x * img->width + y) * 3;

			px |= img->data[ idx ] << 16;
			px |= img->data[ idx + 1 ] << 8;
			px |= img->data[ idx + 2 ];

			break;
		default:
			exit(0);
			return 0;
	}
	return px;
}

/* Data Convert ============================================================= */
void image_mat2gray(const Matrix * Mat, Image ** Gray) {
	int i;

	_verify(Gray);
	if (*Gray == NULL) (*Gray) = image_new(Mat->columns, Mat->rows, IMAGE_PALETTE_GRAY);

	for (i = 0; i < Mat->size; i++) {
		(*Gray)->data[i] = (unsigned char) Mat->data[i];
	}
}

void image_gray2mat(const Image * Gray, Matrix ** Mat) {
	long int i;

	_verify(Mat);
	if (*Mat == NULL) (*Mat) = matrix_new(Gray->height, Gray->width);

	for (i = 0; i < Gray->size; i++) {
		(*Mat)->data[i] = (double) Gray->data[i];
	}
}

/* Color Convert ============================================================ */
void image_rgb2gray(const Image * RGB, Image ** Gray) {
	//void imp_rgb24_to_gray8( int width, int height, const unsigned char * rgb, unsigned char * gray ) {

	double px;
	int row, col;

	int width;
	int height;

	const unsigned char * rgb_data;
	unsigned char * gray_data;

	_verify(Gray);
	if (*Gray == NULL) (*Gray) = image_new(RGB->width, RGB->height, IMAGE_PALETTE_GRAY);

	width = RGB->width;
	height = RGB->height;

	rgb_data = RGB->data;
	gray_data = (*Gray)->data;

	for (row = 0; row < height; row++) {
		for (col = 0; col < width; col++) {
			/*
			 idx = (row * width + col)*3;
						px = 0.2125 * rgb[idx];
						px += 0.7154 * rgb[idx + 1];
						px += 0.0721 * rgb[idx + 2];
			 */

			/* Matlab coeficients */
			px = 0.2989 * (*rgb_data++);
			px += 0.5870 * (*rgb_data++);
			px += 0.1140 * (*rgb_data++);

			/*  */
			//px = 0.2125 * (*rgb_data++);
			//px += 0.7154 * (*rgb_data++);
			//px += 0.0721 * (*rgb_data++);

			px = ROUND(px);
			if (px > 255) px = 255;
			*gray_data = (unsigned char) px;
			gray_data++;
		}
	}
}

void image_gray2rgb(const Image * Gray, Image ** RGB) {
	//void imp_rgb24_to_gray8( int width, int height, const unsigned char * rgb, unsigned char * gray ) {

	int row, col;

	int width;
	int height;

	unsigned char * rgb_data;
	unsigned char * gray_data;

	_verify(RGB);
	if (*RGB == NULL) (*RGB) = image_new(Gray->width, Gray->height, IMAGE_PALETTE_RGB);

	width = Gray->width;
	height = Gray->height;

	gray_data = Gray->data;
	rgb_data = (*RGB)->data;

	for (row = 0; row < height; row++) {
		for (col = 0; col < width; col++) {
			/*
			 idx = (row * width + col)*3;
						px = 0.2125 * rgb[idx];
						px += 0.7154 * rgb[idx + 1];
						px += 0.0721 * rgb[idx + 2];
			 */

			*rgb_data = *gray_data;
			rgb_data++;
			
			*rgb_data = *gray_data;
			rgb_data++;
			
			*rgb_data = *gray_data;
			rgb_data++;
			
			gray_data++;
			
		}
	}
}

/* Static functions ========================================================= */
static void constructor(Image * img, int width, int height, ImagePalette palette) {
	img->width = width;
	img->height = height;

	/* constructor */
	switch (palette) {
		case IMAGE_PALETTE_GRAY:
			img->bands = 1;
			img->size = width * height;

			break;
		case IMAGE_PALETTE_YUV420p:
			img->bands = 1;
			img->size = width * height * 1.5;

			break;
		case IMAGE_PALETTE_RGB:
			img->bands = 3;
			img->size = width * height * 3;

			break;
		default:
			exit(0);
			return;
	}
	img->palette = palette;

	/* interface */
	switch (palette) {
		case IMAGE_PALETTE_GRAY:
		case IMAGE_PALETTE_YUV420p:
			img->methods.set_pixel = set_pixel_gray;
			img->methods.get_pixel = get_pixel_gray;

			break;
		case IMAGE_PALETTE_RGB:
			img->methods.set_pixel = set_pixel_rgb;
			img->methods.get_pixel = get_pixel_rgb;

			break;
		default:
			exit(0);
			return;
	}


}

static void set_pixel_gray(Image * img, int x, int y, int px) {
	img->data[y * img->width + x] = (unsigned char) px;
}

static int get_pixel_gray(Image * img, int x, int y) {
	return (int) img->data[x * img->width + y];
}

static void set_pixel_rgb(Image * img, int x, int y, int px) {
	int idx = (y * img->width + x)*3;

	img->data[idx] = (unsigned char) (px >> 16);
	img->data[idx + 1] = (unsigned char) (px >> 8);
	img->data[idx + 2] = (unsigned char) px;
}

static int get_pixel_rgb(Image * img, int x, int y) {
	int px;
	int idx = (x * img->width + y)*3;
	px = 0;
	px |= (img->data[idx++] << 16);
	px |= (img->data[idx++] << 8);
	px |= (img->data[idx]);
	return px;
}
